package com.ai.bitcoin.service.impl;

import com.ai.bitcoin.bo.PayRequest;
import com.ai.bitcoin.constant.CodeConstant;
import com.ai.bitcoin.constant.CommonConstant;
import com.ai.bitcoin.constant.WeiXinPayContants;
import com.ai.bitcoin.jpa.dao.BccOrderDao;
import com.ai.bitcoin.jpa.dao.BccSubPackageDao;
import com.ai.bitcoin.jpa.dao.BccUserDao;
import com.ai.bitcoin.jpa.entity.BccOrder;
import com.ai.bitcoin.jpa.entity.BccSubPackage;
import com.ai.bitcoin.jpa.entity.BccUser;
import com.ai.bitcoin.output.GeneralReponse;
import com.ai.bitcoin.service.PayService;
import com.ai.bitcoin.service.helper.PayServiceHelper;
import com.ai.bitcoin.service.helper.UserServiceHelper;
import com.ai.bitcoin.service.helper.WeiXinPayServiceHelper;
import com.ai.bitcoin.service.helper.WeiXinPayServiceHelper.WeixinCallBackResult;
import com.ai.bitcoin.service.helper.WeixinServiceUpdateHelper;
import com.ai.bitcoin.util.ClassUtil;
import com.ai.bitcoin.util.JacksonUtils;
import com.ai.bitcoin.weixin.DomManager;
import com.ai.bitcoin.weixin.WeixinDom;
import com.ai.bitcoin.weixin.template.BonusChangeTemplate;
import com.ai.bitcoin.weixin.template.SendReportService;
import com.ai.bitcoin.weixin.template.TemplateManager;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.*;

@Service
@Slf4j
public class PayServiceImpl implements PayService {

    @Autowired
    private BccOrderDao bccOrderDao;

    @Autowired
    private BccSubPackageDao bccSubPackageDao;

    @Autowired
    private PayServiceHelper payServiceHelper;

    @Autowired
    private BccUserDao bccUserDao;

    @Autowired
    private UserServiceHelper userServiceHelper;

    @Autowired
    private SendReportService sendReportService;

    @Autowired
    private WeixinServiceUpdateHelper weixinServiceUpdateHelper;

    @Override
    public GeneralReponse prePay(PayRequest payRequest) {

        //step 1:  组装order
        BccOrder order = payRequest.getOrder();
        BccSubPackage subPackage = bccSubPackageDao.findBySymbolAndStatus(order.getSubPackageSymbol(), 1);
        if (subPackage == null) {
            return new GeneralReponse(CodeConstant.BUSINESS_ERROR, null, "symbol is wrong");
        }
        order.setActualAmount(subPackage.getCost());
        order.setTotalAmount(subPackage.getCost());
        order.setInfo(JacksonUtils.toJson(subPackage));

        //step 2:  pay processon
        Double actualCurrency = subPackage.getCost();
        boolean processReq = false;
        if (payRequest.getPayType() == PayRequest.PayType.WEI_XIN_PAY) {
            String openId = payRequest.getWeiXinPayRequest().getOpenId();
            // wei pay
            TreeMap<String, Object> param = new TreeMap<String, Object>();
            String nonce_str = String.valueOf(new Random().nextInt());
            param.put("nonce_str", nonce_str);
            param.put("body", WeiXinPayContants.APPNAME + "[" + subPackage.getName() + "]");
            param.put("out_trade_no", payRequest.getOrderCode());
            param.put("total_fee", Math.round(actualCurrency * 100)); // 微信接口 // 付款单位是分
            param.put("spbill_create_ip", payRequest.getIp());
            param.put("notify_url", WeiXinPayServiceHelper.buildCallBackUrl("1"));
            param.put("trade_type", payRequest.getWeiXinPayRequest().getTradeType());
            param.put("openid", openId);
            Map<String, Object> weiXinPayResult = WeiXinPayServiceHelper.preOrder(param);
            if (StringUtils.equals(String.valueOf(weiXinPayResult.get("returnCode")), WeiXinPayContants.RETURN_CODE_SUCCESS)
                    && StringUtils.equals(String.valueOf(weiXinPayResult.get("resultCode")),
                    WeiXinPayContants.RESULT_CODE_SUCCESS)) {
                processReq = true;
            }
            if (processReq) {
                bccOrderDao.save(order);
                long timeStamp = System.currentTimeMillis() / 1000L;
                Map<String, Object> resp = new HashMap<>();
                resp.put("orderCode", payRequest.getOrderCode());
                resp.put("codeUrl", weiXinPayResult.get("codeUrl"));
                TreeMap<String, Object> args = new TreeMap<>();
                args.put("timeStamp", Long.toString(timeStamp));
                args.put("nonceStr", nonce_str);
                args.put("package", "prepay_id=" + weiXinPayResult.get("prePayId"));
                args.put("signType", "MD5");
                args.put("appId", WeiXinPayContants.APPID);
                args.put("paySign", WeiXinPayServiceHelper.paramSign(args));
                args.remove("appId");
                resp.put("args", args);
                return new GeneralReponse(CodeConstant.SUCCESS, resp);
            }
            else {
                return new GeneralReponse(CodeConstant.PREPAY_FAIL, weiXinPayResult, "预支付失败");
            }
        }
        else {
            return new GeneralReponse(CodeConstant.PARAM_ERROR, null, "payType is error");
        }
    }

    @Override
    public String callBackWeiXinPay(WeixinCallBackResult weixinCallBackResult) {
        if (weixinCallBackResult == null) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("return_code", "FAIL");
            map.put("return_msg", "error");
            return DomManager.createXml(new WeixinDom(), map);
        }
        String returnCode = weixinCallBackResult.getReturn_code();
        if (StringUtils.equals(WeiXinPayContants.RETURN_CODE_SUCCESS, returnCode)) {
            String resultCode = weixinCallBackResult.getResult_code();
            if (StringUtils.equals(WeiXinPayContants.RESULT_CODE_SUCCESS, resultCode)) {
                //step1 : invalidate request sign
                TreeMap<String, Object> sortResultMap = sortWeixinCallBackParamMap(weixinCallBackResult);
                String localSign = WeiXinPayServiceHelper.paramSign(sortResultMap);
                if (!StringUtils.equals(weixinCallBackResult.getSign(), localSign)) {
                    log.error("weixin callback sign is invalid,{ localSign = " + localSign + ",sign = " + weixinCallBackResult.getSign() + "}");
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("return_code", "FAIL");
                    map.put("return_msg", "sign error");
                    return DomManager.createXml(new WeixinDom(), map);
                }
                String transactionId = weixinCallBackResult.getTransaction_id();
                String orderCode = weixinCallBackResult.getOut_trade_no();
                BccOrder order = bccOrderDao.findByOrderCode(orderCode);
                if (order != null) {
                    if (!(Objects.equals(CommonConstant.SUCCESS_PAY, order.getStatus())) && (Objects.equals(order.getStatus(), CommonConstant.UNDONE_PAY))) {
                        Date updateTime = new Date();
                        payServiceHelper.updateOrder(updateTime, CommonConstant.SUCCESS_PAY, transactionId, orderCode, order);

                        BccUser user = bccUserDao.findByOpenId(order.getOpenId());
                        BccSubPackage subPackage = JacksonUtils.fromJson(order.getInfo(), BccSubPackage.class);
                        Map<String, Object> data2 = new HashMap<>();
                        data2.put("openId", order.getOpenId());
                        data2.put("url", "http://explorelife.vip/bitcoin/bitcoin/media/" + order.getOpenId() + ".png");
                        data2.put("first", "您的PLC变更如下");
                        data2.put("FieldName", "增加原因");
                        data2.put("Account", "购买套餐");
                        data2.put("change", "增加");
                        data2.put("CreditChange", subPackage.getBonus());
                        data2.put("CreditTotal", user.getBonus());
                        sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new BonusChangeTemplate(), data2));


                        Double rewardBonusCHild = 50.0;
                        userServiceHelper.updateUserBonus(new Date(), rewardBonusCHild, order.getOpenId());
                        user = bccUserDao.findByOpenId(order.getOpenId());
                        Map<String, Object> data1 = new HashMap<>();
                        data1.put("openId", order.getOpenId());
                        data1.put("url", "http://explorelife.vip/bitcoin/bitcoin/media/" + order.getOpenId() + ".png");
                        data1.put("first", "您的PLC变更如下");
                        data1.put("FieldName", "增加原因");
                        data1.put("Account", "获取收益分成");
                        data1.put("change", "增加");
                        data1.put("CreditChange", rewardBonusCHild);
                        data1.put("CreditTotal", user.getBonus());
                        sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new BonusChangeTemplate(), data1));
                        weixinServiceUpdateHelper.registUserPlcRecord(order.getOpenId(), null, "获取收益分成", 5, rewardBonusCHild);

                        if (user.getParentOpenId() != null && !StringUtils.isBlank(user.getParentOpenId())) {
                            Double rewardBonus = 25.0;
                            userServiceHelper.updateUserBonus(new Date(), rewardBonus, user.getParentOpenId());

                            BccUser parentUser = bccUserDao.findByOpenId(user.getParentOpenId());

                            Map<String, Object> data = new HashMap<>();
                            data.put("openId", user.getParentOpenId());
                            data.put("url", "http://explorelife.vip/bitcoin/bitcoin/media/" + user.getParentOpenId() + ".png");
                            data.put("first", "您的PLC变更如下");
                            data.put("FieldName", "增加原因");
                            data.put("Account", "获取收益分成");
                            data.put("change", "增加");
                            data.put("CreditChange", rewardBonus);
                            data.put("CreditTotal", parentUser.getBonus());
                            sendReportService.sendTemplateMsg(TemplateManager.buildTemplateParater(new BonusChangeTemplate(), data));
                            weixinServiceUpdateHelper.registUserPlcRecord(user.getParentOpenId(), order.getOpenId(), "获取收益分成", 4, rewardBonus);
                        }
                    }
                    else {
                        log.error("the order has been doned [ordercode:{" + orderCode + "}    status:{ " + order.getStatus() + "}]");
                    }
                }
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("return_code", "SUCCESS");
                map.put("return_msg", "OK");
                return DomManager.createXml(new WeixinDom(), map);
            }
            else {
                log.error("weixin confirm fail,{errCode--" + weixinCallBackResult.getErr_code() + " errCodeDesc--"
                        + weixinCallBackResult.getErr_code_des() + "}");
            }
        }
        else {
            log.error("weixin confirm fail,{return_msg--" + weixinCallBackResult.getReturn_msg() + "}");
        }
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("return_code", "SUCCESS");
        map.put("return_msg", "OK");
        return DomManager.createXml(new WeixinDom(), map);
    }

    @Override
    @Async
    public void isPay(String orderCode, String symbol, DeferredResult<String> deferredResult) {
        BccOrder order = bccOrderDao.findByOrderCode(orderCode);
        GeneralReponse response;
        if (order == null) {
            response = new GeneralReponse(CodeConstant.ORDER_NOT_FOUND, null, "订单不存在");
        }
        else if (Objects.equals(order.getStatus(), CommonConstant.UNDONE_PAY)) {
            response = new GeneralReponse(CodeConstant.PAY_UNDONE, null, "未支付");
        }
        else if (Objects.equals(order.getStatus(), CommonConstant.SUCCESS_PAY)) {
            response = new GeneralReponse(CodeConstant.SUCCESS, null, "支付成功");
        }
        else {
            response = new GeneralReponse(CodeConstant.BUSINESS_ERROR, null, "unexpected error");
        }
        deferredResult.setResult(JSONObject.fromObject(response).toString());
    }

    /**
     * @param weixinCallBackResult
     * @return
     */
    private TreeMap<String, Object> sortWeixinCallBackParamMap(WeixinCallBackResult weixinCallBackResult) {

        TreeMap<String, Object> sortResultMap = new TreeMap<String, Object>();

        String[] fieldNames = ClassUtil.getFiledName(weixinCallBackResult);
        if (fieldNames != null && fieldNames.length > 0) {
            for (String fieldName : fieldNames) {
                if (!StringUtils.equals("sign", fieldName)) {
                    Object value = ClassUtil.getFieldValueByName(fieldName, weixinCallBackResult);
                    if (value != null) {
                        sortResultMap.put(fieldName, value);
                    }
                }
            }
            return sortResultMap;
        }
        return null;
    }

}
